package com.warmguard.core.config.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * redis配置
 *objectMapper 配置的重要性：
 * 作用：禁用将日期以 Unix 时间戳格式（如 1609459200000）序列化，而是使用 ISO-8601 格式的字符串（如 2021-01-01T00:00:00Z）。
 * 必要性：ISO-8601 格式更易读且符合标准，便于调试和日志记录。如果不配置，可能会导致日期格式不一致或难以理解
 *
 * 作用：支持 Java 8 引入的时间类型（如 LocalDateTime, LocalDate, LocalTime 等）的序列化和反序列化。
 * 必要性：如果不注册此模块，Jackson 默认无法处理这些新的时间类型，会导致序列化失败或生成错误的 JSON 格式。
 *
 *
 * 作用：允许扩展自定义序列化器和反序列化器。
 * 必要性：虽然当前代码没有添加自定义模块内容，但这是一个预留操作，方便后续扩展。如果你有自定义的时间、枚举等类型的序列化需求，可以在此基础上进行扩展。
 *
 * 作用：在反序列化时忽略 JSON 中未映射到 Java 对象的属性，不会抛出异常。
 * 必要性：避免因 JSON 数据结构变化或包含额外字段而导致反序列化失败。特别是在前后端分离开发中，前端可能传递额外字段，忽略这些字段可以提高系统的灵活性和容错能力
 *
 *
 * 作用：允许序列化空对象而不抛出异常。
 * 必要性：某些情况下，空对象是合法的业务状态。如果不禁用此功能，尝试序列化空对象时会抛出异常，影响系统正常运行。
 *
 * 作用：设置所有属性（包括私有属性）都可被序列化和反序列化。
 * 必要性：默认情况下，Jackson 只会序列化带有 getter/setter 方法的属性。通过此配置，可以确保所有属性都能被正确处理，特别是对于没有显式 getter/setter 的类。
 *
 * 作用：启用默认类型信息，以便在反序列化复杂类型时能够正确识别子类。
 * 必要性：当存储的对象是多态类型（即父类引用指向子类实例）时，反序列化时需要知道具体的子类类型。如果不激活，默认只能反序列化为父类，可能导致数据丢失或类型转换错误
 *
 *
 *
 * 主要目的是确保 ObjectMapper 在处理复杂的 Java 对象与 JSON 之间的转换时，能够应对各种常见问题，如日期格式、未知属性、空对象、多态类型等。如果不进行这些配置，可能会遇到以下问题：
 * 日期格式不一致：导致难以阅读和调试。
 * 时间类型序列化失败：Java 8 的新时间类型无法正确处理。
 * 反序列化失败：JSON 中包含未知属性或空对象时抛出异常。
 * 多态类型反序列化错误：无法正确识别子类类型，导致数据丢失或类型转换错误。
 * 因此，这些配置是必要的，能够提高系统的健壮性和可靠性。如果你的应用场景比较简单，确实可以省略部分配置，但在大多数生产环境中，建议保留这些配置以确保系统的稳定性和兼容性。
 * @author valarchie
 */
@Configuration
@EnableCaching
public class RedisConfig  {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);


        // 这段代码中对 ObjectMapper 进行了详细的配置，主要是为了确保在将 Java 对象序列化为 JSON 格式并存储到 Redis 中时，以及从 Redis 中读取 JSON 并反序列化为 Java 对象时，能够正确处理各种情况。
        ObjectMapper objectMapper = new ObjectMapper();
        // 禁用时间戳格式：disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 确保日期以 ISO-8601 格式序列化
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // JavaTimeModule：支持 Java 8 的时间类型（如 LocalDateTime）。
        objectMapper.registerModule(new JavaTimeModule());
        // SimpleModule：可以扩展自定义序列化器和反序列化器。
        objectMapper.registerModule((new SimpleModule()));
        //有属性不能映射的时候不报错
        // 忽略未知属性：disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) 在反序列化时忽略未知属性。
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        //对象为空时不抛异常
        // 允许空对象：disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) 允许序列化空对象。
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        // 可见性设置：setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY) 设置所有属性都可见。
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 激活默认类型：activateDefaultTyping 启用默认类型信息，方便反序列化复杂类型。
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
            JsonTypeInfo.As.PROPERTY);

        //GenericJackson2JsonRedisSerializer：使用自定义的 ObjectMapper 创建一个 JSON 序列化器，确保 Redis 中存储的对象能够正确序列化和反序列化。
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }


}
